#ifndef PROXY_H
#define PROXY_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "llhttp.h"
#include "uv.h"

//#define DEBUG

#define ssize_t long long
#define strncasecmp _strnicmp

#  define safecalloc(x, y) calloc(x, y)
#  define safemalloc(x) malloc(x)
#  define saferealloc(x, y) realloc(x, y)
#  define safefree(x) (free (x), *(&(x)) = NULL)
#  define safestrdup(x) _strdup(x)

/* Define to the full name of this package. */
#define PACKAGE_NAME "Winproxy"

/* Name of package */
#define PACKAGE "winproxy"

/* Version number of package */
#define VERSION "1.0"


#define DEFAULT_PROXY_ADDR "127.0.0.1"
#define DEFAULT_PROXY_PORT 8080

#define MAX_CLIENTS 10240
/*
 * Port constants for HTTP (80) and SSL (443)
 */
#define HTTP_PORT 80
#define HTTP_PORT_SSL 443

 /*
INADDR_ANY: 0.0.0.0
default: 127.0.0.1:8080
*/
struct config_s {
	//configure paras
	char proxy_addr[16];
	int proxy_port;
	unsigned int idletimeout; //MAX_IDLE_TIME

	int max_clients; //MAX_CLIENTS
	//runtime state
	uv_tcp_t proxy_sock;
};

extern struct config_s gconfig;
extern uv_loop_t* gloop;
extern int gChilds_num; //http connection number

/*
 * Various logable statistics
 */
typedef enum {
	STAT_BADCONN,           /* bad connection, for unknown reason */
	STAT_OPEN,              /* connection opened */
	STAT_CLOSE,             /* connection closed */
	STAT_REFUSE,            /* connection refused (to outside world) */
	STAT_DENIED             /* connection denied to tinyproxy itself */
} status_t;

/*
 * This structure holds the information pulled from a URL request.
 */
struct request_s {
	uint8_t method;
	//Store the incoming request's HTTP protocol
	uint8_t major;
	uint8_t minor;

	char* host;
	uint16_t port;

	char* path;
};

#define MAX_URL_LEN 256
#define MAX_ERROR_MSG_LEN 256

typedef struct {
	uv_write_t req;
	uv_buf_t buf;
} write_req_t;

/*
 * Connection Definition
 */
struct conn_s {
	//between client and proxy
	uv_buf_t cbuf;
	uv_tcp_t client_socket;
	// Store the incoming request's HTTP protocol.
	struct {
		unsigned int major;
		unsigned int minor;
	} protocol;
	int error_number;
	char error_string[MAX_ERROR_MSG_LEN];
	char detail_string[MAX_ERROR_MSG_LEN];

	//between proxy and server
	uv_getaddrinfo_t resolver; //DNS
	int port; //default is 443
	uv_connect_t uv_con;
	uv_tcp_t server_socket;
};

struct llhttps {
	llhttp_t parser;
	//init: set 0
	//parse to end of http header: set 1
	int http_header_finish; //cb_complete
	int found_url; //CONNECT ntp.msn.cn:443 HTTP/1.1
	char url[MAX_URL_LEN]; //cb_url
};


#define SEGMENT_LEN (512)
#define MAXIMUM_BUFFER_LENGTH (1024)
#define RW_BUF_SIZE (64 * 1024)

struct child {
	struct conn_s conn; //connection info
	struct llhttps llhttp;
};


/*
 *Level	Description
 * ---- - ---------- -
 *LOG_CRIT	This is catastrophic.Basically, tinyproxy can not recover
 * from thisand will either close the child(if we're lucky),
 * or the entire daemon.I would relegate this to conditions
 * like unable to create the listening socket, or unable to
 * create a child.If you're going to log at this level provide
 * as much information as possible.
 *
 *LOG_ERR	Okay, something bad happened.We can recover from this, but
 * the connection will be terminated.This should be for things
 * like when we cannot create a socket, or out of memory.
 * Basically, the connection will not work, but it's not enough
 * to bring the whole daemon down.
 *
 *LOG_WARNING	There is condition which will change the behaviour of
 * tinyproxy from what is expected.For example, somebody did
 * not specify a port.tinyproxy will handle this (by using
 * it's default port), but it's a _higher_ level situation
 * which the admin should be aware of.
 *
 *LOG_NOTICE	This is for a special condition.Nothing has gone wrong, but
 * it is more important than the common LOG_INFO level.Right
 * now it is used for actions like creating / destroying children,
 *unauthorized access, signal handling, etc.
 *
 * LOG_CONN	This additional level is for logging connections only, so
 * it is easy to control only the requests in the logfile.
 * If we log through syslog, this is set to LOG_INFO.
 * --hgb
 *
 * LOG_INFO	Everything else ends up here.Logging for incoming
 * connections, denying due to filtering rules, unable to
 * connect to remote server, etc.
 *
 * LOG_DEBUG	Don't use this level. :) Use the two DEBUG?() macros
 * instead since they can remain in the source if needed. (I
 * don't advocate this, but it could be useful at times.)
 */

#define LOG_CRIT	0
#define LOG_ERR		1
#define LOG_WARNING	2
#define LOG_NOTICE	3
#define LOG_CONN	4
#define LOG_INFO	5
#define	LOG_DEBUG	6

extern int glog_level;
extern int load_config(int argc, char** argv, struct config_s* pconfig);

extern int start_proxy_server(struct config_s* pconfig, uv_connection_cb on_new_connection);
extern void exit_proxy_server(struct config_s* pconfig);

extern void set_http_proxy(char* proxy_ip, unsigned short  port);
extern void reset_http_proxy(void);

//stat
extern int update_stats(status_t update_level);
/*
 * This routine logs messages to either the log file or the syslog function.
 */
extern void log_message(int level, const char* fmt, ...);

extern int get_all_headers(uv_tcp_t* s, llhttp_t* parser);

extern int process_request(struct child* pchild, struct request_s* request);

/*
 * Add the error information to the conn structure.
 */
extern void indicate_http_error(struct conn_s* connptr, int number, const char* message, ...);

extern int extract_url(const char* url, int default_port, struct request_s* request);


/*
 * Display an error to the client.
 */
extern int send_http_error_message(struct conn_s* connptr);

/*
 * Open a connection to a remote host.  It's been re-written to use
 * the getaddrinfo() library function, which allows for a protocol
 * independent implementation (mostly for IPv4 and IPv6 addresses.)
 */
extern int opensock(struct child* pchild, const char* host, int port);

extern void on_connect_remote_server(uv_connect_t* req, int status);

/*
* malloc buffer before call stream_write_client
	connptr->cbuf.base = malloc(RW_BUF_SIZE);
	connptr->cbuf.len = RW_BUF_SIZE;
 thie buffer will be free in stream_write_client
*/
extern void stream_write_client(uv_tcp_t* socket, ssize_t count);

#endif
